package com.centanet.framework.network;

import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import com.centanet.framework.interfaces.HttpCallback;
import com.centanet.framework.interfaces.ILoggerTag;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.orhanobut.logger.Logger;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.zip.GZIPInputStream;

/**
 * json数据请求,使用gson解析
 *
 * @since 3.1
 * Created by guilin on 16/5/24.
 */
public class AbsJsonRequest<T> extends Request<T> {

    protected final HttpCallback<T> mHttpCallback;
    protected final Map<String, String> mHeaders;
    protected final byte[] mBody;
    protected final String mBodyContentType;
    protected final Gson mGson;
    protected final TypeToken<T> mTypeToken;

    public AbsJsonRequest(final Builder<T> builder) {
        super(builder.mMethod, builder.mUrl, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                builder.mHttpCallback.error(volleyError);
            }
        });
        this.mHttpCallback = builder.mHttpCallback;
        this.mHeaders = builder.mHeaders;
        this.mBody = builder.mBody;
        this.mBodyContentType = builder.mBodyContentType;
        this.mGson = builder.mGson;
        this.mTypeToken = builder.mTypeToken;
        setTag(builder.tag);
        setRetryPolicy(builder.mRetryPolicy);
        setShouldCache(builder.mShouldCache);
    }

    @Override
    public Map<String, String> getHeaders() {
        return mHeaders;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        return mBody;
    }

    @Override
    public String getBodyContentType() {
        return mBodyContentType;
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse networkResponse) {
        byte[] h = new byte[2];
        h[0] = networkResponse.data[0];
        h[1] = networkResponse.data[1];
        final int headerData = (h[0] << 8) | h[1] & 0xFF;
        StringBuilder builder = new StringBuilder();
        if (headerData != -1 && headerData == 0x1f8b) {
            ByteArrayInputStream bis = new ByteArrayInputStream(networkResponse.data);
            try {
                InputStream in = new GZIPInputStream(bis);
                BufferedReader r = new BufferedReader(new InputStreamReader(in),
                        100);
                for (String line = r.readLine(); line != null; line = r.readLine()) {
                    builder.append(line);
                }
                in.close();
                bis.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            String parsed;
            try {
                parsed = new String(networkResponse.data, HttpHeaderParser.parseCharset(networkResponse.headers, "UTF-8"));
            } catch (UnsupportedEncodingException var4) {
                parsed = new String(networkResponse.data);
            }
            builder.append(parsed);
        }
        Logger.t(ILoggerTag.FRAME_RESPONSE).json(builder.toString());
        try {
            T t = mGson.fromJson(builder.toString(), mTypeToken.getType());
            return Response.success(t, HttpHeaderParser.parseCacheHeaders(networkResponse));
        } catch (JsonSyntaxException e) {
            Logger.t(ILoggerTag.JSON_PARSE_ERROR).e(e, "JsonSyntaxException");
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(T t) {
        mHttpCallback.success(t);
    }

    /**
     * Builder构建
     */
    static class Builder<T> {

        private final Object tag;
        private final int mMethod;
        private final String mUrl;
        private final HttpCallback<T> mHttpCallback;
        private Map<String, String> mHeaders;
        private byte[] mBody;
        private String mBodyContentType;
        private RetryPolicy mRetryPolicy;
        private boolean mShouldCache;
        private Gson mGson;
        private TypeToken<T> mTypeToken;

        /**
         * @param tag          请求标签,用于取消请求
         * @param method       请求方法
         * @param url          地址
         * @param httpCallback 回调
         */
        public Builder(Object tag, int method, String url, HttpCallback<T> httpCallback) {
            this.tag = tag;
            mMethod = method;
            mUrl = url;
            mHttpCallback = httpCallback;
            mRetryPolicy = new DefaultRetryPolicy();
            mShouldCache = false;
            mBodyContentType = "application/x-www-form-urlencoded; charset=UTF-8";
        }

        /**
         * http请求头信息
         */
        public Builder<T> headers(Map<String, String> headers) {
            this.mHeaders = headers;
            return this;
        }

        /**
         * 请求参数
         */
        public Builder<T> params(byte[] params) {
            this.mBody = params;
            return this;
        }

        /**
         * 请求策略,默认{@link DefaultRetryPolicy}
         */
        public Builder<T> retryPolicy(RetryPolicy retryPolicy) {
            this.mRetryPolicy = retryPolicy;
            return this;
        }

        /**
         * the content type of the POST or PUT body.
         */
        public Builder<T> bodyContentType(String bodyContentType) {
            this.mBodyContentType = bodyContentType;
            return this;
        }

        /**
         * 缓存,默认false
         */
        public Builder<T> shouldCache(boolean shouldCache) {
            this.mShouldCache = shouldCache;
            return this;
        }

        /**
         * gson
         */
        public Builder<T> gson(Gson gson) {
            this.mGson = gson;
            return this;
        }

        /**
         * gson泛型解析
         */
        public Builder<T> typeToken(TypeToken<T> typeToken) {
            this.mTypeToken = typeToken;
            return this;
        }

        public AbsJsonRequest<T> build() {
            return new AbsJsonRequest<>(this);
        }
    }
}
